const fs = require("fs");
const EventEmitter = require("events");
const Queue = require("./LinkedList");

class MyWriteStream extends EventEmitter {
  constructor(path, options) {
    super();
    this.path = path;
    this.flags = options.flags || "w";
    this.mode = options.mode || 438;
    this.start = options.start || 0;
    this.end = options.end;
    this.autoClose = options.autoClose || true;
    this.encoding = options.encoding || "utf-8";
    this.highWaterMark = options.highWaterMark || 16 * 1024;

    this.open();
    this.writeOffset = this.start; // 当前文件写入偏移量
    this.writing = false; // 是否正在写入
    this.writeLen = 0; // 当前累计的写入量
    this.needDrain = false; // 是否需要执行drain事件
    this.cache = new Queue(); // 缓存队列
  }

  open() {
    fs.open(this.path, this.flags, this.mode, (err, fd) => {
      if (err) return this.emit("error", err);

      this.fd = fd;
      this.emit("open", this.fd);
    });
  }

  write(chunk, encoding, cb) {
    // 写入内容统一转换为buffer
    chunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);

    this.writeLen += chunk.length;
    let flag = this.writeLen < this.highWaterMark;
    this.needDrain = !flag;

    if (this.writing) {
      // 当前正在执行写入，所以内容应该排队
      this.cache.enQueue({ chunk, encoding, cb });
    } else {
      this.writing = true;
      // 当前不是正在写入那么就执行写入
      this._write(chunk, encoding, () => {
        cb();
        // 清空排队内容
        this._clearBuffer();
      });
    }

    return flag;
  }

  _clearBuffer() {
    let data = this.cache.deQueue();
    if (data) {
      this._write(data.element.chunk, data.element.encoding, () => {
        data.element.cb && data.element.cb();
        this._clearBuffer();
      });
    } else {
      // 队列中没有值
      if (this.needDrain) {
        this.needDrain = false;
        this.emit("drain");
      }
    }
  }

  _write(chunk, encoding, cb) {
    if (typeof this.fd !== "number") {
      return this.once("open", () => {
        // 有参数， 因此要包一层
        return this._write(chunk, encoding, cb);
      });
    }

    fs.write(
      this.fd,
      chunk,
      this.start,
      chunk.length,
      this.writeOffset,
      (err, written) => {
        if (err) return this.emit("error", err);

        this.writeOffset += written;
        this.writeLen -= written;
        cb && cb();
      }
    );
  }
}

let ws = new MyWriteStream("./f9.txt", {
  highWaterMark: 3,
});
// ws.on("open", (fd) => {
//   console.log("open---", fd);
// });

let flag = ws.write("1", "utf-8", () => {
  console.log("ok1");
});

flag = ws.write("11", "utf-8", () => {
  console.log("ok2");
});

ws.on("drain", () => {
  console.log("drain");
});
